package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.HotArticleService;
import com.heima.common.exception.CustException;
import com.heima.feigns.AdminFeign;
import com.heima.model.admin.pojos.AdChannel;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.common.constants.article.ArticleConstants;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.app.AggBehaviorDTO;
import com.heima.utils.common.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class HotArticleServiceImpl implements HotArticleService {

    @Autowired
    private ApArticleMapper apArticleMapper;

    /**
     * 计算热文章
     */
    @Override
    public void computeHotArticle() {
        // 1.查询近五天的文章
        String beginDate = LocalDateTime.now().minusDays(5).format(
                DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        List<ApArticle> articleList = apArticleMapper.selectArticleByDate(beginDate);

        // 2.计算热点文章分值
        List<HotArticleVo> hotArticleVoList = computeArticleScore(articleList);

        //3 为每一个频道缓存热点较高的30条文章
        cacheTagToRedis(hotArticleVoList);
    }

    @Autowired
    AdminFeign adminFeign;

    private void cacheTagToRedis(List<HotArticleVo> hotArticleVoList) {
        ResponseResult<List<AdChannel>> result = adminFeign.findAll();
        if (!result.checkCode()) {
            CustException.cust(AppHttpCodeEnum.REMOTE_SERVER_ERROR, "远程调用频道列表失败");
        }
        List<AdChannel> adChannelList = result.getData();
        if (CollectionUtils.isEmpty(adChannelList)) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST, "频道列表不存在");
        }
        //遍历频道频道列表,筛选当前频道的文章
        for (AdChannel adChannel : adChannelList) {
            List<HotArticleVo> voList = hotArticleVoList.stream()
                    .filter(hotArticle -> hotArticle.getChannelId().equals(adChannel.getId()))
                    .collect(Collectors.toList());
            sortAndCache(voList, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + adChannel.getId());
        }
        // 给推荐频道缓存30条数据  所有文章排序之后的前30条
        sortAndCache(hotArticleVoList, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);

    }

    @Autowired
    StringRedisTemplate redisTemplate;

    private void sortAndCache(List<HotArticleVo> hotArticleVos, String cacheKey) {
        hotArticleVos = hotArticleVos.stream()
                .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                .limit(30)
                .collect(Collectors.toList());
        redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(hotArticleVos));
    }

    /**
     * 计算热点文章分值
     *
     * @param articleList
     * @return
     */
    private List<HotArticleVo> computeArticleScore(List<ApArticle> articleList) {
        return articleList.stream().
                map(apArticle -> {
                    HotArticleVo hotArticleVo = new HotArticleVo();
                    BeanUtils.copyProperties(apArticle, hotArticleVo);
                    Integer score = computeScore(apArticle);
                    hotArticleVo.setScore(score);
                    return hotArticleVo;
                }).collect(Collectors.toList());
    }

    private Integer computeScore(ApArticle apArticle) {
        Integer score = 0;
        if (apArticle == null) {
            CustException.cust(AppHttpCodeEnum.AP_USER_DATA_NOT_EXIST);
        }
        if (apArticle.getLikes() != null) {
            score += ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        if (apArticle.getCollection() != null) {
            score += ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        if (apArticle.getComment() != null) {
            score += ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        if (apArticle.getViews() != null) {
            score += ArticleConstants.HOT_ARTICLE_VIEW_WEIGHT;
        }
        return score;
    }

    /**
     * 重新计算文章热度  更新redis缓存
     *
     * @param mess
     */
    @Override
    public void updateApArticle(AggBehaviorDTO mess) {
        //1查询文章
        ApArticle article = apArticleMapper.selectById(mess.getArticleId());
        if (article == null) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST, "关联的文章数据不存在");
        }
        //2 修改文章的行为数据（阅读1、点赞3、评论5、收藏8）
        if (mess.getView() != 0) {
            article.setViews((int) (article.getViews() == null ? mess.getView() : article.getViews() + mess.getView()));
        }
        if (mess.getCollect() != 0) {
            article.setCollection((int) (article.getCollection() == null ? mess.getCollect() : article.getCollection() + mess.getCollect()));
        }
        if (mess.getLike() != 0) {
            article.setLikes((int) (article.getLikes() == null ? mess.getLike() : article.getLikes() + mess.getLike()));
        }
        if (mess.getComment() != 0) {
            article.setComment((int) (article.getComment() == null ? mess.getComment() : article.getComment() + mess.getComment()));
        }
        apArticleMapper.updateById(article);
        Integer score = computeScore(article);
        // 如果是今天发布的文章，热度*3
        String nowStr = DateUtils.dateToString(new Date());
        String publishStr = DateUtils.dateToString(article.getPublishTime());
        if (nowStr.equals(publishStr)) {
            score = score * 3;
        }
        //4 更新缓存（频道)
        updateArticleCache(article, score, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + article.getChannelId());
        //5 更新推荐列表的缓存
        updateArticleCache(article, score, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);
    }

    /**
     * 更新文章缓存
     *
     * @param article
     * @param score
     * @param cacheKey
     */
    private void updateArticleCache(ApArticle article, Integer score, String cacheKey) {
        String hotArticlesJson = redisTemplate.opsForValue().get(cacheKey);
        if (StringUtils.isNotBlank(hotArticlesJson)) {
            List<HotArticleVo> hotArticleVoList = JSON.parseArray(hotArticlesJson, HotArticleVo.class);
            boolean flag = false;
            // 如果缓存中有文章
            for (HotArticleVo hotArticleVo : hotArticleVoList) {
                if (hotArticleVo.getId().equals(article.getId())) {
                    hotArticleVo.setScore(score);
                    flag = true;
                    break;
                }
            }
            // 缓存当中没有当前文章
            if (!flag) {
                HotArticleVo hotArticle = new HotArticleVo();
                BeanUtils.copyProperties(article, hotArticle);
                hotArticle.setScore(score);
                hotArticleVoList.add(hotArticle);
            }
            hotArticleVoList = hotArticleVoList.stream()
                    .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                    .limit(30)
                    .collect(Collectors.toList());
            redisTemplate.opsForValue().set(cacheKey,JSON.toJSONString(hotArticleVoList));
        }
    }
}
